package com.sw.listener;

import com.sw.dao.BlockDao;
import com.sw.dao.TransactionDao;
import com.sw.pojo.BlockInfo;
import com.sw.pojo.BlockInfo2;
import com.sw.pojo.TransactionInfo;
import com.sw.pojo.USDT;
import com.sw.provider.model.TransactionInfo2;
import com.sw.receiver.Receiver;
import com.sw.util.USDTapi;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.methods.response.EthBlock;
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
import org.web3j.protocol.core.methods.response.EthTransaction;
import org.web3j.protocol.http.HttpService;

import java.io.IOException;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Component
public class BlockListener implements ApplicationRunner {
    Web3j web3j = Web3j.build(new HttpService());

    @Autowired
    private BlockDao blockDao;

    @Autowired
    private TransactionDao transactionDao;

    public void blockRegisterListener(BigInteger blockNum, Receiver receiver) {
        web3j.catchUpToLatestAndSubscribeToNewBlocksObservable(
                DefaultBlockParameter.valueOf(blockNum), true).subscribe(block -> {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String time = sdf.format(new Date(Long.parseLong(String.valueOf(block.getBlock().getTimestamp())) * 1000L));
            BlockInfo2 blockInfo = new BlockInfo2();
            List<TransactionInfo2> txs = new ArrayList<>();
            List<EthBlock.TransactionResult> transactions = block.getBlock().getTransactions();
            for (EthBlock.TransactionResult t : transactions){
                TransactionInfo2 txInfo = new TransactionInfo2();
                BeanUtils.copyProperties(t,txInfo);
                txs.add(txInfo);
            }
            blockInfo.setNumber(block.getBlock().getNumber())
                    .setHash(block.getBlock().getHash())
                    .setParentHash(block.getBlock().getParentHash())
                    .setMiner(block.getBlock().getMiner())
                    .setDifficulty(block.getBlock().getDifficulty())
                    .setSize(block.getBlock().getSize())
                    .setGasLimit(block.getBlock().getGasLimit())
                    .setGasUsed(block.getBlock().getGasUsed())
                    .setTimestamp(time)
                    .setTxs(txs);
            receiver.receive(blockInfo);
        });
    }

    //监听随主程序启动而执行run方法
    @Override
    public void run(ApplicationArguments args) {
        //DS
        BigInteger maxBlock = blockDao.selectMaxNum();
        this.blockRegisterListener(maxBlock, (blockInfo) -> {
                System.out.println(blockInfo);
                try {
                    BlockInfo blockInfoData = new BlockInfo();
                    blockInfoData.setNumber(blockInfo.getNumber())
                            .setHash(blockInfo.getHash())
                            .setParentHash(blockInfo.getParentHash())
                            .setMiner(blockInfo.getMiner())
                            .setDifficulty(blockInfo.getDifficulty())
                            .setSize(blockInfo.getSize())
                            .setGasLimit(blockInfo.getGasLimit())
                            .setGasUsed(blockInfo.getGasUsed())
                            .setTimestamp(blockInfo.getTimestamp());
                    int blockRaw = blockDao.insert(blockInfoData);
                    System.out.println("区块信息插入+"+blockRaw);
                }catch(Exception e) {
                    System.out.println(e.getMessage());
                    System.out.println(String.format("块号:%s,主键冲突",blockInfo.getNumber()));
                }
                try {
                    //eth交易
                    List<TransactionInfo2> txs =  blockInfo.getTxs();
                    if(!CollectionUtils.isEmpty(txs)){
                        txs.forEach(t->{
                            //主币交易入库
                            EthGetTransactionReceipt transactionReceipt = null;
                            try {
                                transactionReceipt = web3j.ethGetTransactionReceipt(t.getHash()).send();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                            TransactionInfo txEntity = new TransactionInfo();
                            BigInteger gasPrice = t.getGasPrice();
                            BigInteger gas = transactionReceipt.getResult().getGasUsed();
                            BigInteger gasCost = gasPrice.multiply(gas);
                            txEntity.setHash(t.getHash())
                                    .setBlockHash(t.getBlockHash())
                                    .setBlockNumber(t.getBlockNumber())
                                    .setFromAddr(t.getFrom())
                                    .setToAddr(t.getTo())
                                    .setValue(String.valueOf(t.getValue()))
                                    .setSymBol("ETH交易")
                                    .setGasPrice(String.valueOf(t.getGasPrice()))
                                    .setGasLimit(String.valueOf(t.getGas()))
                                    .setGasUsed(transactionReceipt.getResult().getGasUsed())
                                    .setGasCost(String.valueOf(gasCost));
                            String status = transactionReceipt.getResult().getStatus();
                            if (status.equals("0x1")){
                                txEntity.setStatus("success");
                            }else {txEntity.setStatus("fail");}
                            int ETHRaw = transactionDao.insert(txEntity);
                            System.out.println("主币交易信息:"+txEntity);
                            System.out.println("主币信息数据+"+ETHRaw);
                        });
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

                //erc20代币交易入库
                List<USDT.TransferEventResponse> ERC20Txs =USDTapi.getUSDTApi().transferEventOb(blockInfo.getNumber());
                    for (USDT.TransferEventResponse erc20Tx :ERC20Txs){
                        EthGetTransactionReceipt transactionReceipt = null;
                        EthTransaction transaction = null;
                        try {
                            transactionReceipt = web3j.ethGetTransactionReceipt(erc20Tx.log.getTransactionHash()).send();
                            transaction = web3j.ethGetTransactionByHash(erc20Tx.log.getTransactionHash()).send();
                        } catch (IOException e) {
                            continue;
                        }
                        BigInteger gasPrice = transaction.getResult().getGasPrice();
                        BigInteger gas = transactionReceipt.getResult().getGasUsed();
                        BigInteger gasCost = gasPrice.multiply(gas);
                        TransactionInfo txEntity = new TransactionInfo();
                        txEntity.setHash(erc20Tx.log.getTransactionHash())
                                .setBlockHash(erc20Tx.log.getBlockHash())
                                .setBlockNumber(erc20Tx.log.getBlockNumber())
                                .setFromAddr(erc20Tx.from)
                                .setToAddr(erc20Tx.to)
                                .setValue(String.valueOf(erc20Tx.value))
                                .setSymBol("USDT交易")
                                .setGasPrice(String.valueOf(gasPrice))
                                .setGasLimit(String.valueOf(gas))
                                .setGasUsed(transactionReceipt.getResult().getGasUsed())
                                .setGasCost(String.valueOf(gasCost));
                        String status = transactionReceipt.getResult().getStatus();
                        if (status.equals("0x1")){
                            txEntity.setStatus("success");
                        }else {txEntity.setStatus("fail");}
                        int ERC20Raw = transactionDao.insert(txEntity);
                        System.out.println("代币交易信息:"+txEntity);
                        System.out.println("代币信息数据+"+ERC20Raw);
                    }
        });
    }
}
